package cn.wgx.commons.security.shiro.realm;

import cn.wgx.commons.security.shiro.token.CustomUsernamePasswordToken;
import cn.wgx.modules.sys.entity.SysPermission;
import cn.wgx.modules.sys.entity.SysRole;
import cn.wgx.modules.sys.entity.SysUser;
import cn.wgx.modules.sys.services.SysPermissionService;
import cn.wgx.modules.sys.services.SysRoleService;
import cn.wgx.modules.sys.services.SysUserService;
import cn.wgx.modules.sys.util.PermissionUtil;
import cn.wgx.modules.sys.util.UserUtil;
import cn.wgx.modules.sys.web.LoginController;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


@Slf4j
public class CustomRealm extends AuthorizingRealm {

    @Resource
    @Lazy
    SysUserService sysUserService;

    @Resource
    @Lazy
    SysRoleService sysRoleService;

    @Resource
    @Lazy
    SysPermissionService sysPermissionService;

    public final static String realmName = "customRealm";

    public static String getRealmName(){
        return realmName;
    }

    /**
     * 微信用户验证
     * @param token
     * @param info
     * @throws AuthenticationException
     */
    @Override
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException{
        /*这里可以写自定义判断token跟info的关系*/
        super.assertCredentialsMatch(token,info);
    }


    /**
     * 获取Info前走一次缓存
     * @param principals
     * @return
     */
    protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals){
        AuthorizationInfo info;

        info = (AuthorizationInfo) UserUtil.getCache(UserUtil.CACHE_AUTH_INFO);

        if (info == null) {
            info = doGetAuthorizationInfo(principals);
            if (info != null) {
                UserUtil.putCache(UserUtil.CACHE_AUTH_INFO, info);
            }
        }

        return info;
    }
    /**
     * 认证 - 验证用户名密码
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        CustomUsernamePasswordToken atoken = (CustomUsernamePasswordToken) token;
        // 校验登录验证码
        if (LoginController.isValidateCodeLogin(atoken.getUsername(), false, false)){
            Session session = UserUtil.getSession();
            String code = (String)session.getAttribute(LoginController.VALIDATE_CODE);
            if (atoken.getCaptcha() == null || !atoken.getCaptcha().toUpperCase().equals(code)){
                throw new AuthenticationException("msg:验证码错误, 请重试.");
            }
        }

        SysUser sysUser;
        int loginType = atoken.getLoginType();
        if(loginType == 1){//根据用户名登录
            sysUser = UserUtil.getUserByUserName(token.getPrincipal().toString(), true);
        }else if(loginType == 2){//邮箱登录
            sysUser = UserUtil.getUserByEmail(token.getPrincipal().toString());
        }else if(loginType == 3){//手机登录
            sysUser = UserUtil.getUserByPhone(token.getPrincipal().toString());
        }else{
            sysUser = null;
        }

        if(null == sysUser){
            throw new UnknownAccountException("msg:用户不存在: " + token.getPrincipal());
        }
        int status = sysUser.getStatus();
        if(status == 1 || status == 3){
            throw new LockedAccountException("msg:用户已锁定: " + token.getPrincipal());
        }

        Principal Principal = new Principal(sysUser);
        SimpleAuthenticationInfo sa = new SimpleAuthenticationInfo(Principal, sysUser.getMima(), ByteSource.Util.bytes(sysUser.getSalt()), getRealmName());
        sysUserService.getUserFilter(sysUser);
        return sa;
    }

    /**
     * 获取权限、角色
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Principal principal = (CustomRealm.Principal)principals.getPrimaryPrincipal();
        PermissionUtil.iniRoleAndPermission(principal);//初始化所有角色和权限内容,从数据库取出
        List<SysRole> roles = principal.getSysRoles_list();
        List<SysPermission> permissions = principal.getSysPermissions_list();

        Set<String> roles_set = new HashSet<>();
        Set<String> permissions_set = new HashSet<>();

        for(SysRole sysRole: roles){
            roles_set.add(sysRole.getRoleKey());
        }
        for(SysPermission sysPermission: permissions){
            permissions_set.add(sysPermission.getPermissionKey());
        }
        SimpleAuthorizationInfo sa = new SimpleAuthorizationInfo();
        //设置角色/权限
        sa.addRoles(roles_set);
        sa.addStringPermissions(permissions_set);

        //不知道为什么
        principal.setSysRoles(roles_set);
        principal.setSysPermissions(permissions_set);

        principal.update();
        return sa;
    }



    /**
     * 获取缓存id
     * @param principals
     * @return
     */
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        return principals.toString();
    }

    /**
     * 授权用户信息
     */
    @Data
    public static class Principal implements Serializable {

        private static final long serialVersionUID = 1L;

        private SysUser user; //用户对象

        private List<SysRole> sysRoles_list;
        private List<SysPermission> sysPermissions_list;

        private Set<String> sysRoles; //角色组
        private Set<String> sysPermissions; //权限组

        private Integer id;
        private String username; // 登录名
        private String nickname; // 昵称

        private String menus;

        private boolean rememberMe;
        private boolean mobileLogin; // 是否手机登录


        public Principal(SysUser user) {
            this.user = user;
            this.id = user.getUser_id();
            this.username = user.getUsername();
            this.nickname = user.getNickname();
            ini();
        }

        @Override
        public String toString(){
            return id+username;
        }

        public void setUser(SysUser sysUser){
            this.user = sysUser;
            update();
        }

        public void setMenus(String menus){
            this.menus = menus;
            update();
        }


        private void ini(){
            if(null != user){
            }
        }

        //手动更新shiro principal信息 到redis
        public void update(){
            Subject subject = SecurityUtils.getSubject();
            PrincipalCollection newPrincipalCollection =
                    new SimplePrincipalCollection(this, realmName);
            subject.runAs(newPrincipalCollection);
        }
    }


}
